/*  sdk-ps-pl330-dma.c - The simplest kernel module.

* Copyright (C) 2013 - 2016 Xilinx, Inc
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.

*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License along
*   with this program. If not, see <http://www.gnu.org/licenses/>.

*/
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/time.h>
		 
#include <asm/uaccess.h>
#include <asm/delay.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
#include <linux/file.h>
#include <linux/seq_file.h>
#include <linux/kobject.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/err.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/cma.h>
#include <linux/time.h>
#include <linux/atomic.h>
#include <linux/delay.h>


#define DEVICE_NAME "dma_driver"
#define ImageReadAddress0			0x40000000  // from pl address of vivado project
//#define ImageReadAddress0			0x18000000  // from pl_interface_reserved node in  ps-pl330-mem-to-mem-system-user.dtsi

#define PL_DMA_ADDRESS


volatile unsigned int *CaptureReadAddr0;
 
struct timeval tb, te;
 
#define MM_SIZE (256)
 
void dma_callback_func(void *dma_async_param);
void dma_read(u32 dma_dst,u32 dma_src,int size);
int  dma_init(u32 s, int size);
void dma_del(void);
 
struct dma_chan *chan;
 //bus address
dma_addr_t dma_src;
dma_addr_t dma_dst;
//virtual address
char *src = NULL;
char *dst = NULL ;
struct dma_device *dev;
struct dma_async_tx_descriptor *tx = NULL;
enum dma_ctrl_flags flags;
dma_cookie_t cookie;
 
//When dma transfer finished,this function will be called.
void dma_callback_func(void *dma_async_param)
{
	int i=0;
	
	printk("[%s][%d] 1441================\r\n", __func__, __LINE__);
	
	for(i=0; i<MM_SIZE; i++)
    {
    	if(*(dst + i) != (char)('a' + i%26))
    	{
    		printk("Failed\n");
    		return;
    	}
    }
	

    printk("PASS\n");
}
 
void dma_read(u32 dst,u32 src,int size)
{

    tx = dev->device_prep_dma_memcpy(chan, dst, src, size, flags);
    if (!tx){
        printk(KERN_INFO "Failed to prepare DMA memcpy");
		return;
    }
 
    tx->callback = dma_callback_func;//set call back function
    tx->callback_param = NULL;
    cookie = tx->tx_submit(tx); 	//submit the desc
    if (dma_submit_error(cookie)){
        printk(KERN_INFO "Failed to do DMA tx_submit");
		return;
    }
	
	printk("[%s][%d] 1111================\r\n", __func__, __LINE__);
    dma_async_issue_pending(chan);//begin dma transfer
	printk("[%s][%d] 2222================\r\n", __func__, __LINE__);
	return;
}
 
// 定义虚拟设备
static struct platform_device my_virtual_dev = {
    .name = "my-virtual-device",
    .id = -1,
};

// 定义平台设备驱动
static struct platform_driver my_driver = {
    .driver = {
        .name = "my-virtual-device",
    },
};

 
int dma_init(u32 s, int size)
{
	dma_cap_mask_t mask;
	int i = 0;
	struct device *mydev = &my_virtual_dev.dev;
	
	printk("[%s][%d] ============ \r\n", __func__, __LINE__);
	
#ifdef PL_DMA_ADDRESS    
        CaptureReadAddr0 = (volatile unsigned int*)ioremap(ImageReadAddress0, MM_SIZE);
	printk("[%s][%d] CaptureReadAddr0 = %x \r\n", __func__, __LINE__, CaptureReadAddr0);
	if(NULL == CaptureReadAddr0)
	{
		printk("[%s][%d] ================\r\n", __func__, __LINE__);
		return 0;
	}
    
	//alloc 512B src memory and dst memory
	dma_src = ImageReadAddress0;
        src = (char*)CaptureReadAddr0;
#else

    printk("[%s][%d] dma_alloc_coherent ================\r\n", __func__, __LINE__);
    src = dma_alloc_coherent(mydev, size, &dma_src, GFP_KERNEL);
	if(src == NULL)
	{
		printk(KERN_INFO "src = NULL! ============== \r\n");
		return -1;
	}
    
#endif    
     
	for (i = 0; i < MM_SIZE; i++){
		*(src + i) = (char)('a' + i%26);
	}
    
	printk(KERN_INFO "dma_src = 0x%x, s = 0x%x, size = %u\n", dma_src, s, size);
	//src = dma_alloc_coherent(NULL, MM_SIZE, &dma_src, GFP_KERNEL);
	dst = dma_alloc_coherent(mydev, size, &dma_dst, GFP_KERNEL);
	if(dst == NULL)
	{
		printk(KERN_INFO "dst = NULL! ============== \r\n");
		return -1;
	}
 
	printk("[%s][%d] ================\r\n", __func__, __LINE__);
	
	dma_cap_zero(mask);
	dma_cap_set(DMA_SLAVE, mask);//direction:memory to memory
	chan = dma_request_channel(mask,NULL,NULL); //request a dma channel
	if(chan == NULL)
	{
		printk(KERN_INFO "chan = NULL! ============== \r\n");
		return -1;
	}
	
	printk("[%s][%d] ================\r\n", __func__, __LINE__);
		
	flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
	dev = chan->device;
	
	printk("[%s][%d] ================\r\n", __func__, __LINE__);
	return 0;
}
 
void dma_del(void)
{
    //free memory and dma channel
    dma_free_coherent(NULL, MM_SIZE, dst, &dma_dst);
    dma_release_channel(chan);
}
 
static int device_open(struct inode *inode, struct file *file)
{
    return 0;
}
 
static int device_close(struct inode *indoe, struct file *file)
{
	printk("device close\n");
    return 0;
}
 
static ssize_t device_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    int ret = 0;
 
    dma_read(dma_dst, dma_src, MM_SIZE);
 
    return ret;
}
 
static struct file_operations device_fops =
{
    .owner = THIS_MODULE,
    .open  = device_open,
    .release = device_close,
    .read = device_read,
};
 
static struct miscdevice MMAP_misc =
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &device_fops,
};
 
static int __init char_device_init( void )
{
	int ret=0;
	int i = 0;
	printk("init module\n");
	struct device *mydev = &my_virtual_dev.dev;

	dma_cookie_t cookie;;

    ret = platform_device_register(&my_virtual_dev);
    if (ret) {
        pr_err("Failed to register virtual device\n");
        return ret;
    }

    // 注册驱动
    ret = platform_driver_register(&my_driver);
    if (ret) {
        pr_err("Failed to register driver\n");
        platform_device_unregister(&my_virtual_dev);
        return ret;
    }
	
	ret = misc_register(&MMAP_misc);
	if(ret)
	{
		printk("Error:misc_register failed!\n");
		return 0;
	}
    
	printk("[%s][%d] ==== ImageReadAddress0 = %x \n", __func__, __LINE__, ImageReadAddress0);
    
    if(dma_init(ImageReadAddress0, MM_SIZE) < 0)
	{
		printk("[%s][%d] ================\r\n", __func__, __LINE__);
		return 0;
	}
 
	
	dma_read(dma_dst, dma_src, MM_SIZE);
		
	printk("[%s][%d] 33333================\r\n", __func__, __LINE__);
    return 0;
}
 
static void __exit char_device_exit( void )
{
    printk(KERN_ALERT"module exit\n");
    misc_deregister(&MMAP_misc);
 
    iounmap(CaptureReadAddr0);
    dma_del();
}
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("DMA_test");
 
module_init(char_device_init);//模块加载
module_exit(char_device_exit);//模块退出


